/*
 * Copyright 2011 Philippe Blanc.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package me.l1k3.ui.client;

import java.util.ArrayList;

import me.l1k3.core.client.Core;
import me.l1k3.fx.client.FXOpen;
import me.l1k3.fx.client.channel.inter.DimensionConstant;
import me.l1k3.fx.client.inter.FXShowAndHide;
import me.l1k3.ui.client.event.ShowAndHidePanelEvent;
import me.l1k3.ui.client.event.ShowAndHidePanelEventHandler;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.ToggleButton;

public class ShowAndHidePanel extends BlockerPanel {
    private ArrayList<ShowAndHideTab> buttons;
    private EventBus bus;
    private Command busCallback;
	private FXShowAndHide effect;
	private ClickHandler clickHandler;
	private ShowAndHidePanelGroup group;
	private boolean visible;

	public ShowAndHidePanel() {
	    this(true);
	}
	
	public ShowAndHidePanel(boolean visible) {
	    this(visible, new FXOpen(DimensionConstant.Direction.VERTICAL));
	}
	
	public ShowAndHidePanel(boolean visible, FXShowAndHide effect) {
		setStyleName("slide-panel");
		this.visible = visible;
		this.effect = effect;
		
		if(isAttached()) {
		    lazyInitialization();
		}
	}
	
	public void setGroup(ShowAndHidePanelGroup group) {
	    removeGroup();
	    
	    this.group = group;
	    if(group!=null) group.add(this);
	}
	
	public void removeGroup() {
        if(group!=null) {
            group.remove(this);
            group = null;
        }
    }
	
	public boolean isVisible() {
		return visible;
	}
	
	public final HandlerRegistration addShowAndHidePanelEventHandler(ShowAndHidePanelEventHandler handler) {
        if(bus==null) {
            bus = Core.getEventBus();
        }
        
        return bus.addHandlerToSource(ShowAndHidePanelEvent.TYPE, this, handler);
    }
	
	public void show() {
	    show(null);
	}
	
	public void show(Command callback) {
        if(group!=null) {
            group.hide();
        }
        
	    visible = true;

        if (buttons != null) {
            for (ShowAndHideTab tab : buttons) {
                tab.show();
            }
        }

        if (bus != null) {
            bus.fireEventFromSource(ShowAndHidePanelEvent.create(this), this);
        }

        if(effect!=null) {
            if(bus!=null) {
                effect.show(500).callback(getBusCallback(callback!=null? callback : null));
            }
            else {
                if(callback!=null) {
                    effect.show(500).callback(callback);
                }
                else {
                    effect.show(500);
                }
            }
        } 
        else {
            if(callback!=null) Scheduler.get().scheduleDeferred(callback);
            if(bus!=null) fireEventFromSource();
        }
	}
	
	public void hide() {
	    hide(null);
	}
	
	public void hide(Command callback) {
	    visible = false;
	    
	    if(buttons!=null) {
	        for (ShowAndHideTab tab : buttons) {
                tab.hide();
            }
	    }
	    
	    if(effect!=null) {
            if(bus!=null) {
                effect.hide(500).callback(getBusCallback(callback!=null? callback : null));
            }
            else {
                if(callback!=null) {
                    effect.hide(500).callback(callback);
                }
                else {
                    effect.hide(500);
                }
            }
        } 
        else {
            if(callback!=null) Scheduler.get().scheduleDeferred(callback);
            if(bus!=null) fireEventFromSource();
        }
	}
	
	private Command getBusCallback(final Command callback) {
        if(callback!=null) {
            return new Command() {
                @Override
                public void execute() {
                    callback.execute();
                    if(bus!=null) fireEventFromSource();
                }
            };
        }
        
        if(busCallback==null) {
            busCallback = new Command() {
                @Override
                public void execute() {
                    fireEventFromSource();
                }
            };
        }
        
        return busCallback;
    }
    
    private void fireEventFromSource() {
        bus.fireEventFromSource(ShowAndHidePanelEvent.create(this), this);
    }
	
    public void attachTab(ShowAndHideTab tab) {
        if(buttons==null) {
            buttons = new ArrayList<ShowAndHideTab>();
        }
        
        tab.attachPanelSlideDown(this);
        buttons.add(tab);
    }
    
    public void detachTab(ShowAndHideTab tab) {
        tab.detachPanelSlideDown(this);
        
        if(buttons!=null) {
            buttons.remove(tab);
        }
    }

    public ClickHandler getClickHandler() {
        if(clickHandler==null) {
            clickHandler = new PanelSlideDownClickHandler();
        }
        
        return clickHandler;
    }
    
    public FXShowAndHide getEffect() {
        return effect;
    }
	
    public Anchor createAnchor(String label, String tip) {
        ShowAndHideTabAnchor tab = new ShowAndHideTabAnchor();
        attachTab(tab);
        if(visible) tab.show(); else tab.hide();
        tab.setText(label);
        tab.setTitle(tip);
        return tab;
    }
    
    public ToggleButton createButton(String label, String tip) {
        return createButton(label, tip, false);
    }
    
	public ToggleButton createButton(String label, String tip, boolean html) {
	    ShowAndHideTabButton tab = new ShowAndHideTabButton();
	    attachTab(tab);
	    if(visible) tab.show(); else tab.hide();
	    
	    if(html) {
	        tab.getUpFace().setHTML(label);
		}
		else {
		    tab.getUpFace().setText(label);
		}
		
	    tab.setTitle(tip);
		return tab;
	}
	
	public ToggleButton createButton(String labelUp, String labelDown, String tip, boolean html) {
	    ToggleButton tab = createButton(labelUp, tip, false);
	    
	    if(html) {
	        tab.getDownFace().setHTML(labelDown);
		}
		else {
		    tab.getDownFace().setText(labelDown);
		}
	    
		return tab;
	}
	
    @Override
    protected void onLoad() {
        super.onLoad();
        lazyInitialization();
    }
    
    protected void toggle() {
        if(!visible) show(); else hide();
    }
	
	private class PanelSlideDownClickHandler implements ClickHandler {
        @Override
        public void onClick(ClickEvent event) {
            toggle();
        }
	}
	
	private void lazyInitialization() {
	    effect.init(getElement());
	    
        if(visible) {
            effect.show();
            
            if(buttons!=null) {
                for (ShowAndHideTab tab : buttons) {
                    tab.show();
                }
            }
        }
        else {
            effect.hide();
            
            if(buttons!=null) {
                for (ShowAndHideTab tab : buttons) {
                    tab.hide();
                }
            }
        }
	}
	
	public interface ShowAndHideTab {
	    public void attachPanelSlideDown(ShowAndHidePanel panel);
	    public void detachPanelSlideDown(ShowAndHidePanel panel);
	    public boolean isVisible();
	    public void hide();
	    public void show();
	}
	
	protected class ShowAndHideTabAnchor extends Anchor implements ShowAndHideTab {
	    private HandlerRegistration handler;
	    
	    public ShowAndHideTabAnchor() {
	        getElement().addClassName("slide-tab");
	    }
	    
	    @Override
	    public void attachPanelSlideDown(ShowAndHidePanel panel) {
	        handler = addClickHandler(panel.getClickHandler());
	    }

	    @Override
	    public void detachPanelSlideDown(ShowAndHidePanel panel) {
	        handler.removeHandler();
	    }

	    @Override
	    public void hide() {
	        getElement().removeClassName("active");
	    }

	    @Override
	    public void show() {
	        getElement().addClassName("active");
	    }
	}
	
	protected class ShowAndHideTabButton extends ToggleButton implements ShowAndHideTab {
	    private HandlerRegistration handler;
	    
	    public ShowAndHideTabButton() {
	        getElement().addClassName("slide-tab");
	    }
	    
	    @Override
	    public void attachPanelSlideDown(ShowAndHidePanel panel) {
	        handler = addClickHandler(panel.getClickHandler());
	    }

	    @Override
	    public void detachPanelSlideDown(ShowAndHidePanel panel) {
	        handler.removeHandler();
	    }

	    @Override
	    public void hide() {
	        setDown(false);
	        getElement().removeClassName("active");
	    }

	    @Override
	    public void show() {
	        setDown(true);
	        getElement().addClassName("active");
	    }
	}
}
